from sqlalchemy.orm import Session
from models import Contribution, SubCompany, User, Project,Plan
from qiskit import QuantumCircuit
from qiskit_aer import Aer
import numpy as np
from logs import Logs

def random_classical_allocation(users: list,total_coin: int):
    total_cons = sum(user.project_contribution for user in users)
    choices = np.random.choice(len(users),total_coin,replace=True,p=[user.project_contribution/total_cons for user in users])
    counts = [0]*len(users)
    for choice in choices:
        counts[choice] += 1
    return counts


def random_quantum_allocation(users: list,total_coin: int):
    # Implement the quantum random allocation algorithm
    ## get the state of the users
    logger = Logs('allocationquantum.log')
    num_qubits = int(np.log2(len(users)))+1
    # use the project contribution as the state, fill the rest with 0
    state = [user.project_contribution for user in users] + [0] * (2**num_qubits - len(users))
    if np.linalg.norm(state) == 0:
        raise ValueError("The state is zero")
    ## normalize the state
    state =  state/np.linalg.norm(state)
    ## apply the quantum random walk algorithm
    qc = QuantumCircuit(len(users))
    qc.initialize(state, range(num_qubits))
    qc.measure_all()
    logger.info(f"Quantum circuit created successfully")
    logger.info(qc.draw(output='text'))
    num_shots = int(total_coin)
    backend = Aer.get_backend('qasm_simulator')
    job = backend.run(qc, shots=num_shots)
    result = job.result()
    countsdict = result.get_counts()
    ## get the counts for each user
    counts = [countsdict.get(str(i), 0) for i in range(len(users))]
    ## calculate the distribution
    return counts
    

def allocate_project_profits(db: Session, project_id: int,project_coins: int):
    # Retrieve the project and related data
    logger = Logs('allocation.log')
    if not db.query(Project).filter_by(id=project_id).first():
        logger.warning(f"Project with id {project_id} not found")
        ## if this is a new project, we use the total contribution value as the project value
        users = db.query(User).all()
        for user in users:
            user.project_contribution = user.total_contribution
        logger.info(f"user project contribution updated successfully")
        logger.table(users)
    else:
        logger.info(f"Allocating profits for project with id {project_id}")
        project = db.query(Project).filter_by(id=project_id).first()
        users = db.query(User).filter_by(id=project_id).all()
        for user in users:
            user.project_contribution = sum(c.value for c in user.contributions if c.project_id == project.id and c.company_id == project.company_id)
        
    # Allocate 80% of the project profit using the voting algorithm
    voting_coins = int(0.8 * project_coins)
    ## get the vote for every user
    plans = db.query(Plan).filter_by(project_id=project_id).all()
    if not plans:
        for user in users:
            user.votes = np.random.randint(0,100)
    else:
        for user in users:
            for plan in plans:
                if user.id in plan.participants:
                    user.votes += plan.votes
    
    voting_distribution = voting_algorithm(users,voting_coins)

    # Allocate 20% of the project profit using the quantum random algorithm
    random_coins = project_coins - sum(voting_distribution)
    quantum_distribution = random_classical_allocation(users,random_coins)
    response = {}
    # Update the contribution values
    for i, user in enumerate(users):
        user.total_contribution += voting_distribution[i] + quantum_distribution[i]
        response[user.id] = user.total_contribution
    db.commit()
    return response

def voting_algorithm(users,voting_coins):
    # Implement the voting algorithm to allocate contribution values
    total_votes = sum(user.votes for user in users)
    return [int(user.votes/ total_votes * voting_coins) for user in users]

